package ddz.core;

import com.kaka.util.MathUtils;

import java.io.Serializable;
import java.util.Arrays;
import java.util.function.Consumer;

/**
 * 牌点相同的牌的簇集
 */
public class Pile implements Serializable {

    int count = 0;
    int point = -1;
    final int[] suits; //由小到大，下标对应SuitType中的value - 1

    public Pile() {
        this.suits = new int[4];
    }

    public int getPoint() {
        return point;
    }

    /**
     * 获取牌的总张数
     *
     * @return 牌总张数
     */
    public int getCount() {
        return count;
    }

    public void annex(Pile other) {
        if (this.point != other.point) return;
        if (other.count <= 0) return;
        for (int i = 0; i < suits.length; i++) {
            int num = this.suits[i] + other.suits[i];
            if (num > 1) continue;
            this.suits[i] = num;
        }
        count += other.count;
    }

    /**
     * 根据牌数字表示获取花色索引
     *
     * @param card
     * @return
     */
    int suitIndex(int card) {
        int suit = Cards.getSuit(card);
        int suitIdx;
        if (suit == SuitType.xiaowang.getId()) {
            suitIdx = 0;
        } else if (suit == SuitType.dawang.getId()) {
            suitIdx = 1;
        } else {
            suitIdx = suit - 1;
        }
        return suitIdx;
    }

    /**
     * 添加一张牌
     *
     * @param card
     * @return
     */
    public boolean addCard(int card) {
        if (card <= 100) return false;
        int pot = Cards.getPoint(card);
        if (pot == 1) {
            throw new Error("不能有牌点为1的牌数据，如为A请明确牌点为14");
        }
        if (point < 0) {
            point = pot;
        }
        if (pot == point) {
            int suitIdx = suitIndex(card);
            if (suits[suitIdx] >= 1) {
                return false;
            }
            suits[suitIdx]++;
            count++;
            return true;
        }
        throw new Error("添加的牌的牌点与限定的牌点不一致！ point ：" + point + " card：" + card);
    }

    public boolean removeCard(int card) {
        if (count <= 0) {
            return false;
        }
        int suitIdx = suitIndex(card);
        if (suits[suitIdx] == 0) {
            return false;
        }
        suits[suitIdx]--;
        count--;
        return true;
    }

    protected void clear() {
        this.point = -1;
        this.count = 0;
        for (int i = 0; i < suits.length; i++) {
            suits[i] = 0;
        }
    }

    public boolean isDaJoker() {
        return point == 0 && suits[1] > 0;
    }

    public boolean isXiJoker() {
        return point == 0 && suits[0] > 0;
    }

    private static final int[] orderIdx = new int[]{3, 2, 1, 0};

    private int buildCard(int suit, int point) {
        int card;
        if (point == 0) {
            if (suit == 1) {
                card = 500;
            } else {
                card = 600;
            }
        } else {
            card = suit * 100 + point;
        }
        return card;
    }

    /**
     * 遍历所有的牌
     *
     * @param action 每张牌访问器
     */
    public void forEach(Consumer<Integer> action) {
        if (point < 0) return;
        if (count == 0) return;
        for (int i = 0; i < orderIdx.length; i++) {
            int index = orderIdx[i];
            int num = suits[index];
            if (num > 0) {
                int suit = index + 1;
                for (int j = 0; j < num; j++) {
                    action.accept(buildCard(suit, point));
                }
            }
        }
    }

    public int getOneCard() {
        if (count == 0) return -1;
        if (point < 0) return -1;
        for (int i = 0; i < orderIdx.length; i++) {
            int index = orderIdx[i];
            int num = suits[index];
            if (num > 0) {
                int suit = index + 1;
                return buildCard(suit, point);
            }
        }
        return -1;
    }

    /**
     * 获取牌数据
     *
     * @return 所有的牌
     */
    public int[] getCards() {
        if (point < 0) return null;
        if (count == 0) return new int[0];
        final int[] idxs = new int[1];
        int[] cards = new int[count];
        forEach((card) -> {
            cards[idxs[0]] = card;
            idxs[0]++;
        });
        return cards;
    }

    /**
     * 随机获取一张牌
     *
     * @return 牌数据
     */
    public int randomCard() {
        if (count == 0) return -1;
        if (point < 0) return -1;
        int suitsLastIdx = suits.length - 1;
        int n = 0;
        while (n < 1000) {
            int idx = MathUtils.random(0, suitsLastIdx);
            int num = suits[idx];
            if (num <= 0) {
                n++;
                continue;
            }
            int suit = idx + 1;
            return buildCard(suit, point);
        }
        return -1;
    }

    @Override
    public String toString() {
        return point + " : " + Arrays.toString(suits);
    }

    @Override
    public int hashCode() {
        int hash = 3;
        hash = 71 * hash + this.count;
        hash = 71 * hash + this.point;
        hash = 71 * hash + Arrays.hashCode(this.suits);
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Pile other = (Pile) obj;
        if (this.count != other.count) {
            return false;
        }
        if (this.point != other.point) {
            return false;
        }
        return Arrays.equals(this.suits, other.suits);
    }

    @Override
    public Pile clone() {
        Pile copy = new Pile();
        copy.point = point;
        copy.count = count;
        System.arraycopy(suits, 0, copy.suits, 0, suits.length);
        return copy;
    }

    /**
     * 比较牌点大小<br>
     *
     * @param other 其它聚合牌对象
     * @return 本牌点比other大为1，小为-1，相等为0
     */
    public int compare(Pile other) {
        //都不为大小王
        if (getPoint() != 0 && other.getPoint() != 0) {
            if (getPoint() == other.getPoint()) return 0;
            if (getPoint() == 2) return 1;
            if (getPoint() >= 3 && other.getPoint() >= 3) {
                if (getPoint() > other.getPoint()) {
                    return 1;
                } else if (getPoint() == other.getPoint()) {
                    return 0;
                }
            }
            return -1;
        }
        //都为大小王
        if (getPoint() == 0 && other.getPoint() == 0) {
            if (suits[1] > 0 && other.suits[0] > 0) {
                return 1;
            }
            if (suits[0] > 0 && other.suits[0] > 0) {
                return 0;
            }
            if (suits[1] > 0 && other.suits[1] > 0) {
                return 0;
            }
            return -1;
        }
        //一方为大小王
        if (getPoint() == 0) {
            return 1;
        }
        return -1;
    }

    /**
     * 自己大返回1，相同返回0，否则返回-1
     *
     * @param otherCard
     * @return
     */
    public int compare(int otherCard) {
        if (otherCard <= 0) return 1;
        int otherPoint = Cards.getPoint(otherCard);
        //都不为大小王
        if (getPoint() != 0 && otherPoint != 0) {
            if (getPoint() == otherPoint) {
                return 0;
            }
            if (getPoint() == 2) {
                return 1;
            }
            if (getPoint() >= 3 && otherPoint >= 3) {
                if (getPoint() > otherPoint) {
                    return 1;
                } else if (getPoint() == otherPoint) {
                    return 0;
                }
            }
            return -1;
        }
        //都为大小王
        if (getPoint() == 0 && otherPoint == 0) {
            int selfSuit = suits[0] > 0 ? 5 : (suits[1] > 0 ? 6 : 0);
            int otherSuit = Cards.getSuit(otherCard);
            if (selfSuit == otherSuit) {
                return 0;
            }
            if (selfSuit > otherSuit) {
                return 1;
            }
            return -1;
        }
        //一方为大小王
        if (getPoint() == 0) {
            return 1;
        }
        return -1;
    }

    /**
     * 本聚合牌的牌点与other的牌点差是否为1
     *
     * @param other 其它聚合牌对象
     * @return true，牌点差为1
     */
    public boolean isSequenceOf(Pile other) {
        if (other == null) {
            return false;
        }
        if (getPoint() == 2 || other.getPoint() == 2) {
            return false;
        }
        if (getPoint() == 0 || other.getPoint() == 0) {
            return false;
        }
        int p1 = this.getPoint();
        int p2 = other.getPoint();
        if (p1 == 1) p1 = 14;
        if (p2 == 1) p2 = 14;
        return Math.abs(p1 - p2) == 1;
    }

}